package com.ming.liteflow.core.el;

import cn.hutool.core.util.StrUtil;
import com.ming.common.beetl.cache.CacheUtil;
import com.ming.common.util.CommonUtil;
import com.ming.liteflow.core.graph.GraphEL;
import com.ming.liteflow.core.graph.Node;
import com.ming.liteflow.core.node.NodeInfo;
import com.ming.liteflow.execption.LiteFlowELException;
import com.yomahub.liteflow.builder.el.ELBus;
import com.yomahub.liteflow.builder.el.ELWrapper;
import com.yomahub.liteflow.builder.el.ThenELWrapper;
import com.yomahub.liteflow.builder.el.WhenELWrapper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

public class FlowConvertEL {

    public static ELWrapper logicFlow(GraphEL graphEL) throws LiteFlowELException {
        //完全串行
        if(graphEL.isAllThen()){
            return ELBus.then(nodeSetToArray(graphEL));
        }
        //完全并行
        if(graphEL.isAllWhen()){
            return ELBus.when(nodeSetToArray(graphEL));
        }
        //前置组件处理
        //后置组件处理

        //单起点嵌套处理
        ELWrapper wrapper = ELBus.then();
        Node startNode = graphEL.getStartNode();
        flow(wrapper, startNode, graphEL.getEndNode(), graphEL);
        return wrapper;
    }

    public static void flow(ELWrapper wrapper, Node currNode, Node endNode, GraphEL graphEL) throws LiteFlowELException {
        if(currNode == endNode){
            if(graphEL.getReverseList().get(endNode).size() > 1){
                flowThen(wrapper,currNode,endNode,graphEL);
            }
            return;
        }
        Map<Node, List<Node>> nodeMap = graphEL.getList();
        Node forkNode = GraphEL.getForkNode(currNode, nodeMap);
        if(currNode == forkNode || forkNode == null){
            forkNode = GraphEL.getJoinNode(currNode, graphEL);
        }
        Node sNode = currNode;
        Node eNode = forkNode;
        List<Node> nodes = nodeMap.get(sNode);
        if(nodes.size() > 1){
            flowWhen(wrapper,sNode,eNode,graphEL);
        }else{
            flowThen(wrapper,sNode,eNode,graphEL);
        }
        flow(wrapper, forkNode, endNode, graphEL);
    }

    public static void flowThen(ELWrapper wrapper, Node startNode, Node endNode, GraphEL graphEL) throws LiteFlowELException {
        List<Node> list = getStartEndNode(startNode, endNode, graphEL);
        if(wrapper instanceof ThenELWrapper){
            ThenELWrapper thenELWrapper = (ThenELWrapper) wrapper;
            thenELWrapper.then(nodeListToArray(list,graphEL));
        }
    }

    public static void flowWhen(ELWrapper wrapper, Node startNode, Node endNode, GraphEL graphEL) throws LiteFlowELException {
        if(startNode == graphEL.getStartNode() || graphEL.isXNode(startNode)){
            if(wrapper instanceof ThenELWrapper){
                ThenELWrapper thenELWrapper = (ThenELWrapper) wrapper;
                thenELWrapper.then(ELBus.then(getELWrapper(startNode,graphEL)));
            }
        }
        List<List<Node>> allPaths = graphEL.getAllPaths(startNode, endNode, true);
        Node joinNode = graphEL.getJoinNode(startNode);
        List<List<List<Node>>> groupList = graphEL.handlerPaths(allPaths);
        WhenELWrapper whenELWrapper = ELBus.when();
        for (List<List<Node>> listList : groupList){
            if(listList.size() == 1){
                whenELWrapper.when(ELBus.then(nodeListToArray(listList.get(0),graphEL)));
            }else{
                ThenELWrapper thenELWrapper = ELBus.then();
                flow(thenELWrapper, listList.get(0).get(0), joinNode, graphEL);
                whenELWrapper.when(thenELWrapper);
            }
        }
        graphEL.setGroupNodeProp(startNode,whenELWrapper);
        if(wrapper instanceof ThenELWrapper){
            ThenELWrapper thenELWrapper = (ThenELWrapper) wrapper;
            thenELWrapper.then(whenELWrapper);
        }
    }

    private static Object getELWrapper(Node node, GraphEL graphEL) throws LiteFlowELException {
        Map<Long, NodeInfo> nodeInfoMap = graphEL.getNodeInfoMap();
        Long id = node.getProperties().getId();
        if(id != null){
            NodeInfo nodeInfo = nodeInfoMap.get(id);
            return NodeInfoToELUtil.toELWrapper(nodeInfo);
        }
        String componentId = node.getProperties().getComponentId();
        if(StrUtil.isBlank(componentId)){
            throw new LiteFlowELException("节点组件ID为空");
        }
        return componentId;
    }

    public static Object[] nodeSetToArray(GraphEL graphEL) throws LiteFlowELException {
        Set<Node> nodeSet = graphEL.getList().keySet();
        return nodeSet.stream().map(m -> {
            try {
                return getELWrapper(m,graphEL);
            } catch (LiteFlowELException e) {
                throw new RuntimeException(e);
            }
        }).toArray();
    }

    public static Object[] nodeListToArray(List<Node> nodeList,GraphEL graphEL) throws LiteFlowELException {
        return nodeList.stream().map(m -> {
            try {
                return getELWrapper(m,graphEL);
            } catch (LiteFlowELException e) {
                throw new RuntimeException(e);
            }
        }).toArray();
    }

    public static <T> boolean isPropertyAllEqual(List<T> list, Function<T, Object> getProperty, Object value) {
        return list.stream().allMatch(item -> getProperty.apply(item).equals(value));
    }

    // 获取 分叉节点 -> 聚合节点
    public static List<Node> getForkJoinNode(Node forkNode,Node joinNode,GraphEL graphEL){
        return graphEL.getCommonNodesInAllPaths(forkNode,joinNode);
    }

    //获取 开始节点 -> 结束节点
    public static List<Node> getStartEndNode(Node startNode,Node endNode,GraphEL graphEL){
        Map<Node, List<Node>> map = graphEL.getList();
        List<Node> nodeList = new ArrayList<>();
        if(startNode == endNode){
            nodeList.add(startNode);
        }else{
            while (startNode != endNode){
                nodeList.add(startNode);
                startNode = map.get(startNode).get(0);
            }
            nodeList.add(endNode);
        }
        return nodeList;
    }

}
